package ch.uzh.ifi.seal.ssqa.importer.code;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.preference.IPreferenceStore;

import ch.uzh.ifi.seal.ssqa.SsqaPlugin;
import ch.uzh.ifi.seal.ssqa.model.Composite;
import ch.uzh.ifi.seal.ssqa.model.DiagramComponent;
import ch.uzh.ifi.seal.ssqa.model.ModelFactory;
import ch.uzh.ifi.seal.ssqa.model.Module;
import ch.uzh.ifi.seal.ssqa.model.Source;
import ch.uzh.ifi.seal.ssqa.popup.actions.ImportSoftwareModelAction;
import ch.uzh.ifi.seal.ssqa.preferences.PreferenceConstants;
import ch.uzh.ifi.seal.ssqa.storage.IStorage;
import ch.uzh.ifi.seal.ssqa.storage.TeneoHibernateStorage;
/**
 * This is the main importer class of the source code model importer
 * @author marckorsgen
 *
 */
public class SourceCodeModelProvider {
	
	List<Composite> composites = new ArrayList<Composite>();
	HashMap<String, Source> sources = new HashMap<String, Source>();
	
	Map<String, String> subentities;
	
	String presentDiagramComponent; 
	
	/**
	 * The initial method, to start the code model import
	 * @see ImportSoftwareModelAction#run(org.eclipse.jface.action.IAction)
	 * It is also initializing the SubEntityParser, which is used to filter files that are separately mapped again. 
	 * It is omitting the case that a file is parsed twice.
	 * @param diagramSources
	 */
	public void createSourceCodeModel(List<DiagramComponent> diagramSources){
		
		this.subentities = new SubEntityParser(diagramSources).getSubEntities();
		
		for(DiagramComponent diagramComponent : diagramSources){
			this.presentDiagramComponent = diagramComponent.getIdentifier();
			for(String source : diagramComponent.getSourcePaths()){
				IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(source);
				//FIXME IF resource is not in the current workspace
				// WORKAROUND
				IPreferenceStore store = SsqaPlugin.getDefault().getPreferenceStore();
				String pathStr = store.getString(PreferenceConstants.CC_VIEW_DEVICE) + ":" 
						+ store.getString(PreferenceConstants.CC_PATH_TO_SOURCE) + "\\" + source;
				File file = new File(pathStr);
				if(file.exists()){
					this.readOutCodeData(file, null);
				}else{
					System.out.println("Error in reading file: " + pathStr);
				}
				
			}
		}
		this.createIncludeMapping();
	}
	
	public void createSourceCodeModel(List<DiagramComponent> diagramSources, IProgressMonitor monitor){
		try{
			this.subentities = new SubEntityParser(diagramSources).getSubEntities();
			int totalWork = 0;
			if(diagramSources !=null){
				for(DiagramComponent diagramComponent : diagramSources){
					totalWork += diagramComponent.getSourcePaths().size();				
				}
			}
			monitor.beginTask("Create Source Code Model Starts", totalWork);
			for(DiagramComponent diagramComponent : diagramSources){
				this.presentDiagramComponent = diagramComponent.getIdentifier();
				for(String source : diagramComponent.getSourcePaths()){
					IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(source);
					//FIXME IF resource is not in the current workspace
					// WORKAROUND
					IPreferenceStore store = SsqaPlugin.getDefault().getPreferenceStore();
					source.replaceFirst("/", "");
					String pathStr = store.getString(PreferenceConstants.CC_VIEW_DEVICE) + ":" 
							+ store.getString(PreferenceConstants.CC_PATH_TO_SOURCE) + "\\" + source;
					File file = new File(pathStr);
					if(file.exists()){
						this.readOutCodeData(file, null);
					}else{
						System.out.println("Error in reading file: " + pathStr);
					}
					
				}
			}
			this.createIncludeMapping();
			
		}finally{
			monitor.done();
		}
	}
	
	/**
	 * starts the storage of the model to the database through the TeneoHibernateStorage
	 */
	public void persistModel(){
		IStorage storage = new TeneoHibernateStorage();
		storage.saveOrUpdateEntity(composites);
	}
	
	/**
	 * Acually start the data analysis. The method makes a difference, if the file is a folder or a source file.
	 * @param file
	 * @param parent
	 */
	private void readOutCodeData(File file, Composite parent){
		boolean subEntityCheck = true;
		IPath location= Path.fromOSString(file.getAbsolutePath());
		IFile iFile = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(location);

		if(iFile != null){
			if(this.subentities.keySet().contains(iFile.getFullPath().toString())){
				if(!this.subentities.get(iFile.getFullPath().toString()).equals(this.presentDiagramComponent)){
					subEntityCheck = false;
				}
			} 
		}
		
		if(subEntityCheck){
			if(file.isDirectory()) {
				Module module = ModelFactory.eINSTANCE.createModule();
				module.setDiagramMapping(this.presentDiagramComponent);
				
				if(iFile != null){
					module.setFilePath(iFile.getLocation().toString());
					module.setProjectPath("/"+iFile.getProjectRelativePath().toString());
				} else {
					IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(location.lastSegment());
					if(project != null){
						IPath path  = project.getFullPath();
						if(path != null){
							//module.setFilePath(project.getLocation().toString());
							module.setFilePath(path.toOSString());
							module.setProjectPath("/"+project.getName());
						}else{
							System.out.println("[Model Provider]: " + project.getName() + " GetFullPath is null");
						}
						
					}
				}
				
				File[] subFiles = file.listFiles();
				module.setName(file.getName());
				for(File subFile : subFiles){
					this.readOutCodeData(subFile, module);
				}
				//To eliminate simple doc folders from the model
				if(module.getChildren().size()>0){
					this.composites.add(module);
					if(parent != null){
						parent.getChildren().add(module);
						module.setParent(parent);
					}
				}
			} else if(file.isFile() && this.isCFile(file.getName()) && iFile !=null	) {
				SourceCodeAnalyser sfa = new SourceCodeAnalyser(iFile);
				Source source = (Source) sfa.getSource();
				source.setDiagramMapping(this.presentDiagramComponent);
				if(parent != null){
					parent.getChildren().add(source);
					source.setParent(parent);
				}
				this.sources.put(source.getName(), source);
			}
		}
	}
	
	/**
	 * Because for the mapping of two Souces of the EMF model, we need both objects, the mapping can not be done till all the Sources are already created.
	 */
	private void createIncludeMapping(){
		for(Source source : this.sources.values()){
			for(String includeString : source.getIncludeNames()){
				if(this.sources.get(includeString) != null){
					source.getIncludes().add(this.sources.get(includeString));
				}
			}
		}
	}
	
	/**
	 * Checks wether the file is a source file of C/C++ or any other file, that does not have to be parsed. 
	 * @param fileName
	 * @return
	 */
	private boolean isCFile(String fileName){
		if(	fileName.lastIndexOf(".") > 0
			&& (fileName.substring(fileName.lastIndexOf(".")+1).equals("cpp") 
			|| fileName.substring(fileName.lastIndexOf(".")+1).equals("h") 
			|| fileName.substring(fileName.lastIndexOf(".")+1).equals("c"))){
			return true;
		} else {
			return false;
		}
	}
}
